package com.dib.neo.bkfs.fs;

import org.apache.zookeeper.data.Stat;

import java.nio.ByteBuffer;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

public class FileInfo {

  static final int BLOCKSIZE_64K = 64 * 1024;
  static final int BLOCKSIZE_128K = 128 * 1024;
  static final int BLOCKSIZE_256K = 256 * 1024;
  static final int BLOCKSIZE_512K = 512 * 1024;
  static final int BLOCKSIZE_1M = 1024 * 1024;
  static final int BLOCKSIZE_2M = 2 * BLOCKSIZE_1M;
  static final int BLOCKSIZE_4M = 4 * BLOCKSIZE_1M;

  private final BKFileAttributes attributes = new BKFileAttributes();
  private LinkedList<Long> blocks = new LinkedList<>();
  private final String path;

  private Stat stat;

  public FileInfo(String path) {
    this(path,BLOCKSIZE_1M, 0L);
  }

  public FileInfo(String path,int blkSize, long size) {
    this.path = path;
    attributes.setSize(size);
    attributes.setBlockSize(blkSize);

  }

  void setStat(Stat stat) {
    this.stat = stat;
    if(stat!=null){
      attributes.ctime = stat.getCtime();
      attributes.mtime = stat.getMtime();
    }
  }

  public boolean exists(){
    return stat != null;
  }

  public Stat getStat() {
    return stat;
  }

  public synchronized void syncData(Stat stat,byte[] data){
    setStat(stat);
    read(data);
  }

  public String getPath() {
    return path;
  }


  public BKFileAttributes getAttributes() {
    return attributes;
  }

  public int getVersion() {
    return stat==null?0:stat.getVersion();
  }

  public void setSize(long size){
    attributes.setSize(size);
  }

  public void setIno(long ino){
    attributes.setIno(ino);
  }

  public int getBlockCount() {
    return blocks.size();
  }

  public List<Long> getBlocks() {
    return Collections.unmodifiableList(blocks);
  }

  public long getBlock(int index) {
    return blocks.get(index);
  }

  public void truncate(long size){
    int blockNum = (int)Math.ceil(1.0*size / getAttributes().getBlockSize());
    while(blocks.size()>blockNum){
      blocks.removeLast();
    }
    setSize(size);
  }

  public long setBlock(int index, long address) {
    while (index >= blocks.size()) {
      blocks.add(-1L);
    }
    return blocks.set(index, address);
  }

  public byte[] data() {
    byte[] attrs = attributes.data();
    int headLen = 8 + attrs.length;
    ByteBuffer buffer = ByteBuffer.allocate(headLen + 8 * this.getBlockCount());
    buffer.putInt(0);
    buffer.putInt(attrs.length);
    buffer.put(attributes.data());
    for (int index = 0; index < blocks.size(); index++) {
      buffer.putLong(blocks.get(index));
    }
    return buffer.array();
  }

  void read(byte[] data) {
    if(data!=null&&data.length>32){
      ByteBuffer buffer = ByteBuffer.wrap(data);
      int ver = buffer.getInt();
      int attrLen = buffer.getInt();
      byte[] attrs = new byte[attrLen];
      buffer.get(attrs);
      attributes.read(attrs);
      int headLen = 8 + attrs.length;
      blocks.clear();
      for (int index = headLen; index < buffer.limit(); index += 8) {
        blocks.add(buffer.getLong());
      }
    }else{
      attributes.setIno(0);
      attributes.setSize(0);
      blocks.clear();
    }


  }


  @Override
  public String toString() {

    return "{" +
        " ino=" + attributes.fileKey() +
        ", blockSize=" + attributes.getBlockSize() +
        ", size=" + attributes.size() +
        ", ctime=" + attributes.creationTime() +
        ", mtime=" + attributes.lastModifiedTime() +
        ", atime=" + attributes.lastAccessTime() +
        ", blocks=" + blocks +
        '}';
  }

}
